package S3

import (
	"bytes"
	"fmt"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/aws/aws-sdk-go/service/s3/s3manager"
	"server/global"
	CustomerModel "server/model/customer"
)

// 保存每个分片的上传结果
type ChunkUploadResult struct {
	ETag       string
	PartNumber int64
}

// SaveFileToS3 完整上传文件至S3
func SaveFileToS3(fileType, fileName string, fileBytes []byte) error {
	s3Cfg := global.TD27_CONFIG.S3

	body := bytes.NewReader(fileBytes)

	k := fmt.Sprintf("%s/%s/%s", s3Cfg.FileDir, fileType, fileName)
	acl := "public-read"
	if fileType == "file" {
		acl = "private"
	}
	params := &s3manager.UploadInput{
		Bucket: aws.String(s3Cfg.BucketName),
		Key:    aws.String(k),
		Body:   body,
		ACL:    aws.String(acl),
	}
	_, err := global.TD27_S3.Uploader.Upload(params)
	return err
}

// DeleteFileOnS3 删除S3的文件
func DeleteFileOnS3(filetype, key string) (msg string) {
	s3Cfg := global.TD27_CONFIG.S3
	k := fmt.Sprintf("%s/%s/%s", s3Cfg.FileDir, filetype, key)
	params := &s3.DeleteObjectInput{
		Bucket: aws.String(s3Cfg.BucketName),
		Key:    aws.String(k),
	}
	_, err := global.TD27_S3.Svc.DeleteObject(params)
	if err != nil {
		msg = fmt.Sprintf("S3上删除失败,error: %s", err)
		global.TD27_LOG.Error(msg)
		return
	}
	return "删除成功！"
}

// InitiateMultipartUpload 大文件分片上传初始化接口
func InitiateMultipartUpload(fileType, fileName string) (string, error) {
	s3Cfg := global.TD27_CONFIG.S3
	k := fmt.Sprintf("%s/%s/%s", s3Cfg.FileDir, fileType, fileName)
	input := &s3.CreateMultipartUploadInput{
		Bucket: aws.String(s3Cfg.BucketName),
		Key:    aws.String(k),
	}

	result, err := global.TD27_S3.Svc.CreateMultipartUpload(input)
	if err != nil {
		return "", err
	}
	return *result.UploadId, nil
}

// UploadChunkToS3 上传分片文件至S3
func UploadChunkToS3(fileType, fileHash, fileName, uploadID string, chunkNumber int64, fileBytes []byte) (err error) {
	s3Cfg := global.TD27_CONFIG.S3
	// 设置上传到 S3 的参数
	k := fmt.Sprintf("%s/%s/%s", s3Cfg.FileDir, fileType, fileName)
	body := bytes.NewReader(fileBytes)
	result, err := global.TD27_S3.Svc.UploadPart(&s3.UploadPartInput{
		Bucket:     aws.String(s3Cfg.BucketName),
		Key:        aws.String(k),
		Body:       body,
		UploadId:   aws.String(uploadID),
		PartNumber: aws.Int64(chunkNumber),
	})
	if err != nil {
		return
	}
	chunkUploadModel := CustomerModel.ChunkUploadModel{
		UploadID:   uploadID,
		ETag:       aws.StringValue(result.ETag),
		PartNumber: chunkNumber,
		FileHash:   fileHash,
	}

	// 执行添加操作
	err = global.TD27_DB.Create(&chunkUploadModel).Error

	return
}

// CompleteMultipartUpload 合并分片
func CompleteMultipartUpload(fileType, fileName, uploadID string) (err error) {
	s3Cfg := global.TD27_CONFIG.S3
	k := fmt.Sprintf("%s/%s/%s", s3Cfg.FileDir, fileType, fileName)

	// 获取上传块文件结果列表
	var chunkUploadModels []ChunkUploadResult
	if err := global.TD27_DB.Model(&CustomerModel.ChunkUploadModel{}).
		Where("upload_id = ?", uploadID).
		Order("part_number").
		Find(&chunkUploadModels).Error; err != nil {
		return err
	}

	// 构建分片信息
	var parts []*s3.CompletedPart
	for _, chunkUpload := range chunkUploadModels {
		parts = append(parts, &s3.CompletedPart{
			ETag:       aws.String(chunkUpload.ETag),
			PartNumber: aws.Int64(chunkUpload.PartNumber),
		})
	}

	_, err = global.TD27_S3.Svc.CompleteMultipartUpload(&s3.CompleteMultipartUploadInput{
		Bucket:   aws.String(s3Cfg.BucketName),
		Key:      aws.String(k),
		UploadId: aws.String(uploadID),
		MultipartUpload: &s3.CompletedMultipartUpload{
			Parts: parts,
		},
	})

	if err == nil {
		err = global.TD27_DB.Where("upload_id = ?", uploadID).Unscoped().Delete(&CustomerModel.ChunkUploadModel{}).Error
	}
	return
}

// CheckFileExistsS3 检查文件是否存在S3上
func CheckFileExistsS3(fileType, key string) (bool, error) {
	s3Cfg := global.TD27_CONFIG.S3
	fileKey := fmt.Sprintf("%s%s/%s", s3Cfg.FileDir, fileType, key)
	// 检查文件是否存在S3上
	params := &s3.HeadObjectInput{
		Bucket: aws.String(s3Cfg.BucketName),
		Key:    aws.String(fileKey),
	}

	_, err := global.TD27_S3.Svc.HeadObject(params)
	if err != nil {
		// 文件不存在
		return false, err
	}

	// 文件存在
	return true, nil
}
